#ifndef _PART_GROUP_H_
#define _PART_GROUP_H_

#include <vector>
#include "particle_2d.h"
#include "part_geom.h"
#include "ui/ui_sprite.h"
#include "math/vec4.h"
class Image;
class ImageSubset;

namespace script
{
	class WriteLua;
};
namespace LuaPlus
{
    class LuaObject;
    class LuaState;
};

namespace part2d
{
class PartDie;
class P2DForce;
class Part2D;
class PartGroupCache;

/**
 * Particles for 2D Games (about time)
 */
class	PartGroup
{
public:
	PartGroup(Part2D*					pSystem);
	~PartGroup();
	bool					Load(LuaPlus::LuaObject& Obj);
	bool					Save(script::WriteLua& WL);
	void					Clone(PartGroup* pClone);
	bool					Render(const math::Vec2& Offset, float Scale, const math::Vec2& Min = math::Vec2(0,0), const math::Vec2& Max = math::Vec2(1,1) );
    bool					Tick(float dt);
	Particle2D*	 			TriggerParticle(const math::Vec2& ScreenPos);
	Particle2D*				TriggerParticle(const math::Vec2& ScreenPos, const math::Vec2& InitialDir);
	Particle2D*				TriggerParticleAbs(const math::Vec2& ScreenPos, const math::Vec2& InitialDir);
	Particle2D*				TriggerParticleOnLine(const math::Vec2& From, const math::Vec2& To);
	void					SetMaxC(int	Count);
	int						GetMaxC() const { return m_MaxC; };
	void					SetGenRate(float f) { m_GenRate = f; };
	float					GetGenRate() const { return m_GenRate; };
	void					SetFuzzyBirth(const math::Vec2& Area) { m_FuzzyBirth = Area; };
	const math::Vec2&		GetFuzzyBirth() const { return m_FuzzyBirth; };

	//void					SetSystem(Part2D* pSystem) { m_pSystem = pSystem; }
	void					SetSize(const math::Vec2& FromMin, const math::Vec2& FromMax, const math::Vec2& ToMin, const math::Vec2& ToMax);
	const math::Vec2&		GetSizeFromMin() const { return m_SizeFromMin; };
	const math::Vec2&		GetSizeFromMax() const { return m_SizeFromMax; };
	const math::Vec2&		GetSizeToMin() const { return m_SizeToMin; };
	const math::Vec2&		GetSizeToMax() const { return m_SizeToMax; };
	const math::Vec2&		GetMinSpeed() const { return m_MinSpeed; };
	const math::Vec2&		GetMaxSpeed() const { return m_MaxSpeed; };
	float					GetMinSpeedMag() const { return m_MinSpeedMag; };
	void					SetInitialSpeed(const math::Vec2& Min, const math::Vec2& Max, float MinMag);
	const math::Vec2&		GetLifespan() const { return m_Lifespan; };
	void					SetLifespan(const math::Vec2& v) { m_Lifespan = v; };
	void					SetAdditive(bool b) { m_Additive = b; };
	bool					GetAdditive() { return m_Additive; };
	void					SetLockRect(bool b) { m_LockRect = b; };
	bool					GetLockRect() { return m_LockRect; };
	void					SetLockSize(bool b) { m_LockSize = b; };
	bool					GetLockSize() { return m_LockSize; };
	float					GetStretchSpeed() { return m_StretchSpeed; };
	void					SetStretchSpeed(float f) { m_StretchSpeed = f; };
	void					SetImage(const std::string& Image);
	const std::string&		GetImage() const;
	void					AddColor(const math::Vec4& Color) { m_BaseColors.push_back(Color); RecalcColors(); };
	void					AddColor3f(float r, float g, float b, float a) { m_BaseColors.push_back(math::Vec4(r, g, b, a)); RecalcColors(); };
	void					ClearColors() { m_BaseColors.resize(0); RecalcColors(); };
	std::vector<math::Vec4>&	GetColors() { return m_BaseColors; };
	void					RecalcColors();
	void					ClearParticles();
	void					ResetTimer() { m_GenTime = 0.0f; }

	math::Vec2				GetInitialSpeed() const;
	void					SetSpeed(Particle2D*	 pPart, const math::Vec2& Sp);

	bool					IsInside(const math::Vec2& Pos);
	bool					IsActive() const { return m_IsActive; };
	void					SetActive(bool b) { m_IsActive = b; };
	int						GetPartC() const { return m_PartC; }
	
	bool					ShouldCreateParticle(float dt);
	PartGeom*				GetCollision() { return &m_Collision; };

	bool					TickCreation(float dt, const math::Vec2& Pos, const math::Vec2& Size); 
	void					SetSizeScale(float SizeScale);

	/// register with LUA Bind
	static	void			Register(LuaPlus::LuaState* _pScript);

	// callbacks
	typedef void	(*ParticleCallback) (Particle2D*, void*); // callback for effect param

	enum					PARTICLE_CALLBACKS
	{
		PC_DEAD,
		PC_COUNT
	};
	void					SetParticleCB(PARTICLE_CALLBACKS Set, ParticleCallback pCB, void *pData);
	bool					IsSecondary() const { return m_IsSecondary; };
	void					SetSecondary(bool b) {m_IsSecondary = b; };
	const std::string&		GetName() const { return m_Name; };
	int						GetTriggerIndex() const { return m_TriggerIndex; };
	void					SetTriggerIndex(int i) { m_TriggerIndex = i; };

	P2DForce*				CreateForce(const std::string& Name);
	void					DeleteForce(P2DForce*	pForce);
	P2DForce*				GetForce(int Idx) { return m_Forces[Idx]; }
	int						GetForceC() { return (int)m_Forces.size(); }

	friend class PartGroupCache;

private:
	struct					ParticleCallbackData
	{
		ParticleCallbackData() : 
			pCB(0),
			pData(0)
		{
			
		}
		
		ParticleCallback	pCB;
		void*				pData;
	};
	ParticleCallbackData	m_CallbackA[PC_COUNT];

	Particle2D*				GetFreeParticle();
    P2DForce*				GetForce(LuaPlus::LuaObject _LObj);

    // Particles
	Part2D*					m_pSystem;
    Particle2D*             m_Particles;        /// Particles in set
	int						m_MaxC, m_PartC;
    unsigned int            m_LastActive;
	math::Vec2				m_FuzzyBirth;
	math::Vec2				m_SizeFromMin, m_SizeFromMax;
	math::Vec2				m_SizeToMin, m_SizeToMax;
	math::Vec2				m_MinSpeed, m_MaxSpeed;
	float					m_MinSpeedMag;
	math::Vec2				m_Lifespan;

    /// additive blending ?
    bool                    m_Additive;
	bool					m_LockRect;	// use rectangular size
	bool					m_LockSize;	// retain same with/height for entire duration
	bool					m_IsActive;
	bool					m_RenderDebug;

	bool					m_IsSecondary;
	int						m_TriggerIndex;
	// Forces
    std::vector<P2DForce*> m_Forces;
    
    // Texture
	std::string				m_Image;
	ui::UISprite			m_Sprite;

	/// stretch particle based on speed
	float					m_StretchSpeed;

	static const int		COLOR_TABLE = 256;
	COLOR					m_ColorA[COLOR_TABLE];
	std::vector<math::Vec4>	m_BaseColors;

	float					m_GenRate;
	float					m_GenTime;

	PartGeom				m_Collision;
	PartDie*				m_pOnDie;

	std::string				m_Name;
	float					m_SizeScale;
};

}
#endif // _PART_GROUP_H_